﻿using AnalyStock.DataManage;
namespace AnalyStock.GrapImage;
/// <summary>
///     ChartStockPage.xaml 的交互逻辑
/// </summary>
public partial class ChartCandle : UserControl
{
    private readonly int lineCount_Xaxis = 4;
    private readonly double unitMargin = 10.0f;

    // 画K线图界面的长宽设定
    private (double Top, double Left, double Right, double Width, double Height) candlePanelSize;
    private int index_MaxHigh, index_MinLow;

    /// <summary>
    ///     鼠标移动，显示面板信息和鼠标信息指示同步变化
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private double lastPostion_Y;

    //计算加载数据集中的最高价，最低价；
    private double maxVal_High, minVal_Low, maxVal_Vol;

    //单个蜡烛图占有的像素宽度
    private double singleCandleSize_Width;
    public int totalIndex;

    //1个相对单位价格占的像素高，一个交易日索引占的像素宽度。
    private double unitPriceSize_High, unitIndexSize_Width;

    //交易量面板的高度，K线MACD、KDJ等指标面板的高度
    private double volPanelSize_High, indexPanelSize_High;

    /// <summary>
    ///     画蜡烛线
    /// </summary>
    public ChartCandle()
    {
        InitializeComponent();
        InitializePanel_Price();
        MouseEventCanvas.MouseMove += MouseEvent_MouseMove;
        MouseEventCanvas.MouseLeftButtonDown += MouseEvent_MouseDown;
        MouseEventCanvas.MouseLeave += MouseEvent_MouseLeave;
        SizeChanged += (s, e) => PaintKline();
    }

    private void PaintKline()
    {
        if (currCandlePoints.IsNullOrEmpty())
        {
            return;
        }

        IniPaintPanelRect(); //初始化面板边界范围
        IniUnitPointsOnIndex(); // 初始化每个索引的单位高度像素点，单位宽度像素点；
        PaintMarkLineOnXaxis(); //画横轴的价格线
        PaintCandleSeries(); //画蜡烛图与交易量柱状图
        TextMarkOnMaxMinValue(); //画最高值，最低值
        PaintLine_MainChart(); //改变，画均线
        PaintLine_AttachedChart(); //改变，画指标线
    }

    private void IniPaintPanelRect()
    {
        candlePanelSize = (
            Top: 2 * unitMargin,
            Left: 3 * unitMargin,
            Right: 3 * unitMargin,
            Width: CandleCanvas.ActualWidth - (6 * unitMargin),
            Height: CandleCanvas.ActualHeight - (2 * unitMargin)
        );
        volPanelSize_High = VolCanvas.ActualHeight;
        indexPanelSize_High = IndexLineCanvas.ActualHeight;
        ClearAnyCanvas();

    }
    private void ClearAnyCanvas()
    {
        //蜡烛线面板
        CandleCanvas.Children.Clear();
        //均线面板
        MALineCanvas.Children.Clear();
        //鼠标十字线面板
        CrossLineCanvas.Children.Clear();
        //交易量面板
        VolCanvas.Children.Clear();
        //k线辅助指标面板，KDJ、MACD...
        IndexLineCanvas.Children.Clear();

    }
    private void IniUnitPointsOnIndex()
    {
        GetMaxMinValue();
        totalIndex = currCandlePoints.Count < 60 ? 60 : currCandlePoints.Count;
        unitPriceSize_High = maxVal_High != minVal_Low
            ? candlePanelSize.Height / (maxVal_High - minVal_Low)
            : candlePanelSize.Height / maxVal_High; //以最低点为起点，单位价格的高度点
        unitIndexSize_Width = candlePanelSize.Width / totalIndex; //单位索引的宽度点
        singleCandleSize_Width = unitIndexSize_Width < 2.50f ? 0.5f : unitIndexSize_Width - 2.0f;
    }

    //获得最高价最大值、最低价的最小值及索引值；交易量最大值
    private void GetMaxMinValue()
    {
        var _maxPoint = currCandlePoints.MaxBy(n => n.High);
        var _minPoint = currCandlePoints.MinBy(n => n.Low);
        maxVal_High = _maxPoint.High;
        minVal_Low = _minPoint.Low;
        index_MaxHigh = _maxPoint.LocationIndex;
        index_MinLow = _minPoint.LocationIndex;
        maxVal_Vol = currCandlePoints.Max(n => n.Vol);
    }

    //画蜡烛面板上X轴向的暗色价格五分线
    private void PaintMarkLineOnXaxis()
    {
        var _unitSectSize_High = candlePanelSize.Height / lineCount_Xaxis;
        var _unitSect_Price = (maxVal_High - minVal_Low) / lineCount_Xaxis;
        for (var i = 0; i <= lineCount_Xaxis; i++)
        {
            var y_Line = candlePanelSize.Top + candlePanelSize.Height - (i * _unitSectSize_High);
            new Line
            {
                X1 = candlePanelSize.Left,
                Y1 = y_Line,
                X2 = candlePanelSize.Left + candlePanelSize.Width,
                Y2 = y_Line,
                Stroke = Brushes.DarkGray,
                StrokeThickness = 0.2,
                Opacity = 0.7
            }.WithPanel(CandleCanvas);
            new TextBlock
            {
                Text = $"{minVal_Low + (i * _unitSect_Price):F2}",
                //FontFamily ="",
                FontSize = 9,
                Foreground = Brushes.DarkCyan
            }.SetPostion(y_Line - 6.0, 1.0)
                .WithPanel(CandleCanvas);
        }
    }

    /// <summary>
    ///     画连续的K线图
    /// </summary>
    private void PaintCandleSeries()
    {
        ///在区间内画蜡烛线
        currCandlePoints.ForEach(n =>
        {
            PaintSingleCandleStick(n);
            PaintSingleVolStick(n);
        });
        //交易量平均线，随交易柱一起画
        PaintMultiVolMaLine();
    }

    ///画单个蜡烛线形
    private void PaintSingleCandleStick(CandlePoint singleCandle)
    {
        var _xPosition = GetPosition_X(singleCandle.LocationIndex, false);
        var _xUperToLowerShadowline = _xPosition + (singleCandleSize_Width / 2.0f);
        var _yPosition =
        (
            Open: GetPosition_Y(singleCandle.Open, true),
            Close: GetPosition_Y(singleCandle.Close, true),
            Top: GetPosition_Y(singleCandle.High, true),
            Down: GetPosition_Y(singleCandle.Low, true)
        );
        var _candleStickHigh = Math.Abs(_yPosition.Open - _yPosition.Close);
        if (_candleStickHigh < 2)
        {
            _candleStickHigh = 2;
        }

        //画最高价和最低价的中影线,穿透了蜡烛柱体
        new Line
        {
            X1 = _xUperToLowerShadowline,
            Y1 = _yPosition.Top,
            X2 = _xUperToLowerShadowline,
            Y2 = _yPosition.Down,
            Stroke = singleCandle.CandleColor,
            StrokeThickness = 1
        }.WithPanel(CandleCanvas);
        //画开盘和收盘主体框
        new Rectangle
        {
            Stroke = singleCandle.CandleColor,
            StrokeThickness = singleCandle.IsDoubleVol ? 2.0 : 1,
            Fill = singleCandle.IsCloseUpOpen
                ? FindResource("AreaBackColorKLine") as SolidColorBrush
                : singleCandle.CandleColor,
            Width = singleCandleSize_Width,
            Height = _candleStickHigh
        }.SetPostion(singleCandle.IsCloseUpOpen
            ? _yPosition.Close
            : _yPosition.Open, _xPosition).WithPanel(CandleCanvas);
        //涨停标识
        if (singleCandle.IsSkyrocketing)
        {
            new TextBlock
            {
                Text = "🚩", // "●",
                FontSize = 7 * (unitIndexSize_Width / 8.0f),
                Foreground = Brushes.DarkOrange
            }.SetPostion(_yPosition.Top - (12.0f * (unitIndexSize_Width / 8.0f)),
                    _xUperToLowerShadowline - (unitIndexSize_Width / 2.0f))
                .WithPanel(CandleCanvas);
        }
    }

    ///画单个交易量柱状图
    private void PaintSingleVolStick(CandlePoint singleCandle)
    {
        var _xPosition = GetPosition_X(singleCandle.LocationIndex, false);
        var _yPosition = GetPosition_Y(singleCandle.Vol, false);
        var _volRectHigh = volPanelSize_High - _yPosition;
        var _volRectWidth = singleCandleSize_Width;
        new Rectangle
        {
            Stroke = singleCandle.VolColor,
            StrokeThickness = 0.5,
            Width = _volRectWidth,
            Height = _volRectHigh,
            Fill = singleCandle.IsDoubleVol ? singleCandle.VolColor : null
        }.SetPostion(_yPosition, _xPosition).WithPanel(VolCanvas);
    }

    //画最高值，最低值。在蜡烛面板上
    private void TextMarkOnMaxMinValue()
    {
        new TextBlock
        {
            Text = $"<{maxVal_High:F2}>",
            FontSize = 9,
            Foreground = Brushes.Red
        }.SetPostion(GetPosition_Y(maxVal_High, true) - 10.0,
                GetPosition_X(index_MaxHigh, false) - (2.0 * unitIndexSize_Width))
            .WithPanel(CandleCanvas);
        new TextBlock
        {
            Text = $"<{minVal_Low:F2}>",
            FontSize = 9,
            Foreground = Brushes.LimeGreen
        }.SetPostion(GetPosition_Y(minVal_Low, true),
                GetPosition_X(index_MinLow, false) - (2.0 * unitIndexSize_Width))
            .WithPanel(CandleCanvas);
    }

    private void PaintCrossLine(int currentIndex, double x_position, double y_position, bool isUptoDown)
    {
        CrossLineCanvas?.Children.Clear();
        if (totalIndex == 0)
        {
            return;
        }
        //显示X轴横向线
        new Line
        {
            X1 = x_position,
            Y1 = 0,
            X2 = x_position,
            Y2 = ActualHeight - indexPanelSize_High,
            Stroke = Brushes.Snow,
            StrokeThickness = 0.3,
            StrokeDashArray = [20, 20]
        }.WithPanel(CrossLineCanvas);
        new Line
        {
            X1 = 0,
            Y1 = y_position,
            X2 = candlePanelSize.Width + candlePanelSize.Left,
            Y2 = y_position,
            Stroke = Brushes.Snow,
            StrokeThickness = 0.3,
            StrokeDashArray = [20, 20]
        }.WithPanel(CrossLineCanvas);

        //显示均值ma5 ma10 ma20
        new TextBlock
        {
            Text = TextMaValue(currentIndex).Close_MaValue,
            FontSize = 9.5,
            Foreground = Brushes.DarkGray,
            Opacity = 0.7
        }.SetPostion(-5.0, 2.0).WithPanel(CrossLineCanvas);
        //显示交易量ma5 ma10
        new TextBlock
        {
            Text = TextMaValue(currentIndex).Vol_MaValue,
            FontSize = 9.5,
            Foreground = Brushes.DarkGray,
            Opacity = 0.7
        }.SetPostion(CrossLineCanvas.ActualHeight, 2.0).WithPanel(CrossLineCanvas);
        //显示X轴的日期，和Y轴的价格指示
        var _locationXOffset = x_position - (x_position / candlePanelSize.Width * 50);
        new Rectangle
        {
            Fill = Brushes.Peru,
            Width = 50,
            RadiusX = 2,
            RadiusY = 2,
            Height = 12
        }.SetPostion(0.0, _locationXOffset).WithPanel(CrossLineCanvas);

        new TextBlock
        {
            //yyyy-MM-dd
            Text = currCandlePoints[currentIndex].Date.ToDateStrWithGapSymbols(),
            FontSize = 9,
            Foreground = Brushes.White
        }.SetPostion(0.0, _locationXOffset).WithPanel(CrossLineCanvas);
        //显示价格
        var _txtPrice = maxVal_High - ((y_position - candlePanelSize.Top)
            * (maxVal_High - minVal_Low) / candlePanelSize.Height);
        new Rectangle
        {
            Fill = Brushes.Peru,
            Width = 28,
            RadiusX = 2,
            RadiusY = 2,
            Height = 10
        }.SetPostion(y_position - 5.0, 0.0).WithPanel(CrossLineCanvas);
        new TextBlock
        {
            Text = $"{_txtPrice:F2}",
            FontSize = 9,
            Foreground = Brushes.White
        }.SetPostion(y_position - 5.0, 0.0).WithPanel(CrossLineCanvas);
        //如果鼠标向下移动，显示价格较在整个面最高收盘价下降幅度；否则反之
        var downRatio = isUptoDown
            ? (_txtPrice / currCandlePoints[index_MaxHigh].Close) - 1.0
            : (_txtPrice / currCandlePoints[index_MinLow].Close) - 1.0;
        new Rectangle
        {
            Fill = Brushes.Peru,
            Width = 38,
            RadiusX = 2,
            RadiusY = 2,
            Height = 10
        }.SetPostion(y_position - 5.0, CandleCanvas.ActualWidth - candlePanelSize.Right)
            .WithPanel(CrossLineCanvas);
        new TextBlock
        {
            Text = $"{(isUptoDown ? "↓" : "↑")} {downRatio:P1}",
            FontSize = 9,
            Foreground = Brushes.White
        }.SetPostion(y_position - 5.0, CandleCanvas.ActualWidth - candlePanelSize.Right)
            .WithPanel(CrossLineCanvas);
    }

    private static (string Close_MaValue, string Vol_MaValue) TextMaValue(int index)
    {
        return (textMavalue(KlineDataCollection.MaOfKline), textMavalue(KlineDataCollection.MaOfVol));

        string textMavalue(IList<MAPoint>[] lines)
        {
            var _textVal = new StringBuilder();
            lines.ForEach(_line =>
            {
                if (_line.IsNullOrEmpty())
                {
                    return;
                }
                //Type:MA,EMA...;Clcye: 5,10;Value:...
                _textVal.Append($"{_line[index]?.Type}{_line[index]?.Clcye}:{_line[index]?.MAValue:0.00}  ");
            });
            return _textVal.ToString();
        }
    }

    private double GetPosition_Y(double priceorvol, bool isPriceorVol)
    {
        return isPriceorVol
            ? candlePanelSize.Top + ((maxVal_High - priceorvol) * unitPriceSize_High)
            : (1 - (priceorvol / maxVal_Vol)) * volPanelSize_High;
    }

    private double GetPosition_X(int positionIndex, bool isMidpoint = true)
    {
        //判断是否中间点
        return isMidpoint
            ? candlePanelSize.Left + ((positionIndex + 0.5f) * unitIndexSize_Width)
            : candlePanelSize.Left + (positionIndex * unitIndexSize_Width);
    }

    /// <summary>
    ///     通过鼠标在X点像素换算K线的位置索引；
    /// </summary>
    /// <param name="position_X"></param>
    /// <returns></returns>
    private int GetPosition_Index(double position_X)
    {
        return (int)((position_X - candlePanelSize.Left) * totalIndex / candlePanelSize.Width);
    }

    private void MouseEvent_MouseMove(object sender, MouseEventArgs e)
    {
        var _currEle = e.GetPosition(sender as FrameworkElement);
        if (totalIndex == 0)
        {
            return;
        }
        var _index = GetPosition_Index(_currEle.X);
        if (_index < 0 || _index >= currCandlePoints.Count)
        {
            return;
        }
        PaintCrossLine(_index, _currEle.X, _currEle.Y, _currEle.Y > lastPostion_Y);
        lastPostion_Y = _currEle.Y;
        TextPriceTable(_index);
        e.Handled = true;
    }

    private void MouseEvent_MouseLeave(object sender, MouseEventArgs e)
    {
        CrossLineCanvas?.Children.Clear();
        e.Handled = true;
    }
}